﻿#include "stdafx.h"
#include "flow_tracker.h"
#include "title.h"

#include <Hydraulics.h>

using namespace hydraulics;
using namespace std;

struct TABLES{
	static QVector<double> create_DP(){
		QVector<double> v;
		v << 3000 << 6000 << 9000 << 11000 << 15000 << 18000 << 21000 << 25000 << 31000 << 35000; 
		return v;
	}
	static hash_map<int, QVector<double>> create_SP(){
		hash_map<int, QVector<double>> hm;
		hm[26] << 40;
		hm[29] << 40 << 40;
		hm[32] << 35 << 40 << 40;
		hm[35] << 35 << 35 << 40 << 40;
		hm[38] << 30 << 30 << 35 << 40 << 40;
		hm[41] << 25 << 25 << 30 << 30 << 35 << 35;
		hm[45] << 20 << 20 << 20 << 30 << 35 << 35 << 35;
		hm[60] << 10 << 10 << 20 << 20 << 20 << 25 << 25 << 25;
		hm[81] << 3  << 3  << 3  << 15 << 15 << 20 << 20 << 20 << 20;
		hm[101]<< 3  << 3  << 3  << 3  << 3  << 3  << 3  << 3  << 3  << 3;
		return hm;
	}
	static int get_DP_index(const double &curr_DP){
		for(int i = 0; i < DP.size() - 1; ++i){
			if (DP[i] > curr_DP && ( i == 0  || DP[i - 1] < curr_DP)) return i;
		}
		return -1;
	}
	static const QVector<double> DP;
	static const hash_map<int, QVector<double>> SP;
};

const QVector<double> TABLES::DP = TABLES::create_DP();
const hash_map<int, QVector<double>> TABLES::SP = TABLES::create_SP();



FlowTracker::FlowTracker(Common* _common, SceneView* _scene, automaticItem* _parent, const bool display_title)
	: common(_common), scene(_scene), parent(_parent)
{
	init_model();
	vector<QString> list_of_script = scene->common->getScriptList();
	foreach(QString script_name, list_of_script)
		addScriptParameter(script_name);
	connect(common, SIGNAL(set_data_to_automatics(const double &, const double &)), this, SLOT(addLayer(const double &, const double &)));
	state = ANALYSIS;
}

FlowTracker::~FlowTracker(){
}


void FlowTracker::init_model(){
	ParametersModel *model = parent->getModel();
	QString common_parameter = "COMMON";
	model->addItem(common_parameter, "LOCATION//.X", parent->pos().x(), "px", 1, 0, "X position");
	model->addItem(common_parameter, "LOCATION//.Y", parent->pos().y(), "px", 1, 0, "Y position");
	//...........

	
	model->addItem(common_parameter, "DT_DP", 0, "M", 1, 2, "interval recalculation delta_average_pressure");
	model->addItem(common_parameter, "MAX_DP", 0, "kPa", 1, 1, "maximum allowable value delta_average_pressure​​\nfor the transition from ANALYSIS to WORK");

	model->addItem(common_parameter, "MAX_MISMATCH_AB", 0, "m3/h", 1, 1, "maximum allowable mismatch between A and B\nfor the transition from ANALYSIS to WORK");

	model->addItem(common_parameter, "DT_SP", 0, "M", 1, 2, "interval recalculation set_point");
	model->addItem(common_parameter, "MIN_SP", 0, "kPa", 1, 1, "minimum set point value");

	model->addItem(common_parameter, "MIN_DP_PAUSE", 0, "kPa", 1, 1, "minimum value delta_average_pressure\n​​for the transition from WORK to PAUSE");
	model->addItem(common_parameter, "TIME_IN_PAUSE", 0, "M", 1, 1, "maximum time in PAUSE");

}

void FlowTracker::addScriptParameter(const QString &script_name){	
	
}

void FlowTracker::removeScriptParameter(const QString &script_name){

}

void FlowTracker::popupShow(QGraphicsSceneContextMenuEvent *event){
	QMenu menu;	
	QAction* actIndicator = new QAction("indicator", this);
	connect(actIndicator, SIGNAL(triggered()), parent, SLOT(showIndicator()));
	actIndicator->setCheckable(true);
	actIndicator->setChecked(parent->getIndicator()->isVisible());
	QAction* actIndicatorLine = new QAction(/*auto-hide*/"line", this);
	connect(actIndicatorLine, SIGNAL(triggered()), parent, SLOT(showIndicatorLine()));
	actIndicatorLine->setCheckable(true);
	actIndicatorLine->setChecked(parent->getIndicator()->lineIsVisible());
	actIndicatorLine->setEnabled(parent->getIndicator()->isVisible());
	menu.addAction(actIndicator);
	menu.addAction(actIndicatorLine);
	menu.exec(event->screenPos());
}

void FlowTracker::addLayer( const double &time, const double & dt ){
	if (time == 0) state = ANALYSIS;
	vector<autConPoint*> autConPointList = parent->getListOfAutConPoint();
	double A = autConPointList[0]->getOtherItem()->getValueFromData("FLOW");
	double B = autConPointList[1]->getOtherItem()->getValueFromData("FLOW");
	static signed int previous_recalculation_time = 0;
	static double delta_average_pressure = 0;
	static int interval_recalculation_setpoint = parent->getModel()->getNumParameter("DT_DP");
	if (((int)time % interval_recalculation_setpoint == 0 && (int)time != previous_recalculation_time) || time == 0) {
		vector<double> left_sensor, right_sensor;
		for(int index = 2; index < autConPointList.size(); ++index){
			autConPoint* point = autConPointList[index];
			automaticItem *sensor = point->getOtherItem();
			if (sensor != NULL){
				if (sensor->isSensor()){
					if (sensor->getSensor()->isInLeftPartOfPipe()){
						left_sensor.push_back(sensor->getValueFromData("PRESSURE"));
					}else{
						right_sensor.push_back(sensor->getValueFromData("PRESSURE"));
					}
				}else{
					QMessageBox::critical(0, "Error", "Flow Tracker: incorrect connection!", QMessageBox::Ok);	
					return;
				}
			}	
		}
		delta_average_pressure = calc_delta_average_pressure(left_sensor, right_sensor);
		previous_recalculation_time = (int)time;
	}else{

	}
	parent->addDataLayer("DP_AVERAGE", delta_average_pressure);

	

	

	vector<pair<QVector<double>, QVector<double>>> &data = parent->getParameterFromData1("SET_POINTS");
	calc_set_point(time, 0, delta_average_pressure, parent->getModel()->getNumParameter("MIN_SP"), data);

	state = calc_state(time, delta_average_pressure, abs(A - B), false);
	parent->addDataLayer("STATE", (int)state);
	
}

double FlowTracker::calc_delta_average_pressure(const vector<double> &left_sensor, const vector<double> &right_sensor){
	static double average_pressure_left_prev = -1, average_pressure_right_prev = -1;
	double left_sum = 0, right_sum = 0;
	foreach(double value, left_sensor) left_sum += value;
	foreach(double value, right_sensor) right_sum += value;
	double average_pressure_left = left_sum / left_sensor.size();
	double average_pressure_right = right_sum / right_sensor.size();
	double delta_average_pressure = 0;
	if (average_pressure_left_prev != -1 && average_pressure_right_prev != -1){
		delta_average_pressure = qMax(abs(average_pressure_left_prev - average_pressure_left), 
									  abs(average_pressure_right_prev - average_pressure_right));
	}
	average_pressure_left_prev = average_pressure_left;
	average_pressure_right_prev = average_pressure_right;
	return delta_average_pressure;
}

int FlowTracker::calc_set_point(const double &time, const double &mismatch_between_A_and_Qr, const double &delta_average_pressure,
								const double &min_set_point, vector<pair<QVector<double>, QVector<double>>> &data)
{
	int index = 0;
	for (hash_map<int, QVector<double>>::const_iterator it = TABLES::SP.begin(); it != TABLES::SP.end(); ++it){
		double sp = it->first;
		if (sp >= mismatch_between_A_and_Qr){
			data[index].first.clear();
			data[index].second.clear();

			int table_index = TABLES::get_DP_index(delta_average_pressure);
			if (table_index >= 0 && table_index < it->second.size()){
				double length = it->second[table_index];
				data[index].first.push_back(sp - .5);
				data[index].first.push_back(sp);
				data[index].first.push_back(sp);
				data[index].first.push_back(sp - .5);
				data[index].second.push_back(time);
				data[index].second.push_back(time);
				data[index].second.push_back(time + length);
				data[index].second.push_back(time + length);
			}		
		}
		++index;
	}
	if ( state != WORK ) return 0;
	return 1;
}

double FlowTracker::calc_Qr(){
/*
	double Re = reynolds_gravity(params.kinematic_viscosity, Rg, gravity_speed);
	double lambda = hydraulic_resistance_altshul(Re, params.relative_roughness);*/

	return 1;
}

double FlowTracker::calc_mismatch_between_A_and_Qr( const double &A, const double &Qr ){

	return 1;
}

FlowTracker::state_t FlowTracker::calc_state(const double &time, const double &delta_average_pressure, const double &mismatchAandB, const bool &isFailture){
	static double time_in_pause_state = 0;
	static double MAX_DP = parent->getModel()->getNumParameter("MAX_DP");
	static double MAX_MISMATCH_AB = parent->getModel()->getNumParameter("MAX_MISMATCH_AB");
	static double DT_SP = parent->getModel()->getNumParameter("DT_SP");
	static double  MIN_DP_PAUSE = parent->getModel()->getNumParameter("MIN_DP_PAUSE");
	static double TIME_IN_PAUSE = parent->getModel()->getNumParameter("MIN_DP_PAUSE");
	switch (state)
	{
	case FlowTracker::ANALYSIS:
		if (delta_average_pressure <= MAX_DP && mismatchAandB <= MAX_MISMATCH_AB && time >= DT_SP)
			return WORK;
		break;
	case FlowTracker::WORK:
		if (isFailture)
			return FAILURE;
		if (delta_average_pressure >= MIN_DP_PAUSE){
			time_in_pause_state = time;
			return PAUSE;
		}
		break;
	case FlowTracker::PAUSE:
		if (delta_average_pressure < MIN_DP_PAUSE)
			return WORK;
		if (time - time_in_pause_state >= TIME_IN_PAUSE)
			return ANALYSIS;
		break;
	case FlowTracker::FAILURE:

		break;
	default:
		break;
	}

	return state;
}
